package com.swak.boot.config.dubbo;

import static com.swak.boot.Application.APP_LOGGER;
import static com.swak.boot.config.dubbo.DubboUtils.DUBBO_PREFIX;
import static com.swak.boot.config.dubbo.DubboUtils.RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME;
import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;

import org.apache.dubbo.common.Version;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
import org.apache.dubbo.config.spring.context.DubboLifecycleComponentApplicationListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.alibaba.spring.context.config.ConfigurationBeanBinder;
import com.swak.boot.config.dubbo.event.AwaitingNonWebApplicationListener;
import com.swak.boot.config.dubbo.event.DubboConfigBeanDefinitionConflictApplicationListener;
import com.swak.boot.config.dubbo.event.ServiceBeanExportedListener;

/**
 * 简化版本的Dubbo配置，让配置更加的清晰, 删除不需要的配置项 <br>
 * 配合 @EnableDubbo 一起使用
 * 
 * @author lifeng
 */
@Configuration
@ConditionalOnClass({ DubboBootstrap.class })
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
public class DubboAutoConfiguration implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {

	public DubboAutoConfiguration() {
		System.setProperty("netty.epoll.enable", "true");
		APP_LOGGER.debug("Loading Dubbo " + Version.getVersion());
	}

	/**
	 * 处理属性解析
	 * 
	 * @return
	 */
	@ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class)
	@Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME)
	@Scope(scopeName = SCOPE_PROTOTYPE)
	public ConfigurationBeanBinder relaxedDubboConfigBinder() {
		return new BinderDubboConfigBinder();
	}

	/**
	 * 用于防止系统退出
	 * 
	 * @return
	 */
	@Bean
	public AwaitingNonWebApplicationListener awaitingNonWebApplicationListener() {
		return new AwaitingNonWebApplicationListener();
	}

	/**
	 * 清除过多的 ApplicationConfig 注册
	 * 
	 * @return
	 */
	@Bean
	public DubboConfigBeanDefinitionConflictApplicationListener DubboConfigBeanDefinitionConflictApplicationListener() {
		return new DubboConfigBeanDefinitionConflictApplicationListener();
	}

	/**
	 * 服务Bean 的收集
	 * 
	 * @return
	 */
	@Bean
	public ServiceBeanExportedListener ServiceBeanExportedListener() {
		return new ServiceBeanExportedListener();
	}

	/**
	 * 注册Dubbo启动和组件启动的监听器
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		if (applicationContext instanceof ConfigurableApplicationContext) {
			ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
			DubboLifecycleComponentApplicationListener dubboLifecycleComponentApplicationListener = new DubboLifecycleComponentApplicationListener(
					applicationContext);
			context.addApplicationListener(dubboLifecycleComponentApplicationListener);

			DubboBootstrapApplicationListener dubboBootstrapApplicationListener = new DubboBootstrapApplicationListener(
					applicationContext);
			context.addApplicationListener(dubboBootstrapApplicationListener);
		}
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		// Remove the BeanDefinitions of ApplicationListener from
		// DubboBeanUtils#registerCommonBeans(BeanDefinitionRegistry)
		// TODO Refactoring in Dubbo 2.7.9
		removeBeanDefinition(registry, DubboLifecycleComponentApplicationListener.BEAN_NAME);
		removeBeanDefinition(registry, DubboBootstrapApplicationListener.BEAN_NAME);
	}

	private void removeBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
		if (registry.containsBeanDefinition(beanName)) {
			registry.removeBeanDefinition(beanName);
		}
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// DO NOTHING
	}
}